/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2012-2020. All rights reserved.
 * Description: hdc mem common interface.
 * Author: huawei
 * Create: 2020/5/18
 *
 * This program is free software; you can redistribute it and /or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version
 */

#ifndef _HDCDRV_MEM_COM_H_
#define _HDCDRV_MEM_COM_H_

#include <linux/mutex.h>
#include <linux/types.h>
#include <linux/rbtree.h>

#include "hdcdrv_cmd.h"

#ifndef LOG_UT
#include <linux/crc32.h>
#endif

#define VHDC_VCTX_HASH_KEY_INVALID ((unsigned long long)-1)
#define VHDC_MEM_POOL_SG_FLAG 0xE8

#define HDCDRV_SEND_ALLOC_MEM_RETRY_TIME 100

struct hdcdrv_mem {
    void *buf;
};

/*
 * hdc memory pool.
 * size : memory block count
 * segment : memory block size, include the block head
 * head : Point to where the effective memory on the ring begins
 * tail : Point to the released ring position, when the memory is released, put it in this position
 */
struct hdcdrv_mem_pool {
    u32 valid;
    u32 dev_id;
    u32 size;
    u32 segment;
    u32 mask;
    u32 resv;
    u64 head;
    u64 tail;
    struct hdcdrv_mem *ring;
    spinlock_t mem_lock;
    struct list_head wait_list;
};

/*
 * hdc block memory head.
 * magic: magic number, used for check head
 * devid: whith device belongs to
 * type : memory type, HDCDRV_MEM_POOL_TYPE_TX or HDCDRV_MEM_POOL_TYPE_RX
 * size : memory block size, include the block head
 * dma_addr : dma address
 * head_crc : crc32 from magic to dma_addr, used for check head
 * ref_count: the block reference count
 */
struct hdcdrv_mem_block_head {
    u32 magic;
    u32 devid;
    u32 type;
    u32 size;
    dma_addr_t dma_addr;
    u32 head_crc;
    u32 ref_count;
};

#define HDCDRV_MEM_CACHE_LINE   64
#define HDCDRV_MEM_BLOCK_MAGIC  0x48444342 /* HDCB */

/* sizeof(struct hdcdrv_mem_block_head) must less than HDCDRV_MEM_CACHE_LINE */
#define HDCDRV_MEM_BLOCK_HEAD_SIZE HDCDRV_MEM_CACHE_LINE
#define HDCDRV_BLOCK_CRC_LEN offsetof(struct hdcdrv_mem_block_head, head_crc)

#define HDCDRV_BLOCK_HEAD(buf) (struct hdcdrv_mem_block_head *)((char *)(buf) - HDCDRV_MEM_BLOCK_HEAD_SIZE)
#define HDCDRV_BLOCK_DMA_HEAD(addr) (dma_addr_t)((u64)(addr) - HDCDRV_MEM_BLOCK_HEAD_SIZE)

#define HDCDRV_BLOCK_BUFFER(head) (struct hdcdrv_mem_block_head *)((char *)(head) + HDCDRV_MEM_BLOCK_HEAD_SIZE)
#define HDCDRV_BLOCK_DMA_BUFFER(addr_head) (dma_addr_t)((u64)(addr_head) + HDCDRV_MEM_BLOCK_HEAD_SIZE)

struct hdccom_mem_init {
    struct device *dev;
    int pool_type;
    int dev_id;
    u32 segment;
    u32 num;
};

struct hdccom_alloc_mem_para {
    int pool_type;
    int dev_id;
    int len;
    u32 fid;
    bool is_vm;
    struct list_head *wait_head;
};

/* fast mem use define and struct in ctrl->device[devid] */
#define HDCDRV_SYNC_CHECK       1
#define HDCDRV_SYNC_NO_CHECK    0

#define HDCDRV_BUF_LEN 256

#define HDCDRV_LIST_MEM_NUM 1024

#define HDCDRV_DMA_MEM      1
#define HDCDRV_NORMAL_MEM   0

#define HDCDRV_MAX_COST_TIME  100

#define HDCDRV_FRBTREE_FID_BEG 60
#define HDCDRV_FRBTREE_FID_MASK 0xFull
#define HDCDRV_FRBTREE_PID_BEG 0
#define HDCDRV_FRBTREE_PID_MASK 0x0FFFFFFFull
#define HDCDRV_FRBTREE_ADDR_BEG 28
#define HDCDRV_FRBTREE_ADDR_DEL 12
#define HDCDRV_FRBTREE_ADDR_MASK 0x00000FFFFFFFF000ull
#define HDCDRV_HASH_VA_PIDADDR_MASK 0x0FFFFFFFFFFFFFFFull

struct hdcdrv_mem_f {
    void *buf;
    dma_addr_t addr;
    struct page *page;
    u32 len;
    u32 power;
    u32 type;
};

struct hdcdrv_fast_mem {
    int phy_addr_num;
    u32 alloc_len;
    int mem_type;
    u32 page_type;
    int dma_map;
    int devid;
    u64 user_va;
    u64 hash_va;
    struct hdcdrv_mem_f *mem;
};

struct hdcdrv_mem_dfx_stat {
    u64 alloc_cnt;
    u64 alloc_size;
    u64 alloc_normal_size;
    u64 alloc_dma_size;
    u64 free_cnt;
    u64 free_size;
    spinlock_t lock;
};

struct hdcdrv_dev_fmem {
    struct rb_root rbtree;
    struct rb_root rbtree_re;
    spinlock_t rb_lock;
    struct hdcdrv_mem_dfx_stat mem_dfx_stat;
};

struct hdcdrv_mem_fd_list {
    struct hdcdrv_ctx_fmem *ctx_fmem;
    struct hdcdrv_fast_node *f_node;
    struct list_head list;
};

struct hdcdrv_ctx_fmem {
    struct hdcdrv_mem_fd_list mlist;
    spinlock_t mem_lock;
    u64 mem_count;
};

struct hdcdrv_fast_node {
    struct rb_node node;
    u64 hash_va;
    long long pid;
    u32 stamp;
    u32 max_cost;
    atomic_t status;
    struct hdcdrv_mem_fd_list *mem_fd_node;
    struct hdcdrv_fast_mem fast_mem;  // must be last one
};

int hdccom_alloc_mem(struct hdcdrv_mem_pool *pool, void **buf, dma_addr_t *addr);
int hdccom_free_mem(struct hdcdrv_mem_pool *pool, void *buf);
int hdccom_init_mem_pool(struct hdcdrv_mem_pool *pool, struct hdccom_mem_init *init_mem);
int hdccom_free_mem_pool(struct hdcdrv_mem_pool *pool, struct device *dev, u32 segment);
int hdcdrv_mem_block_head_check(void *buf);
void free_mem_pool_single(struct device *dev, u32 segment, struct hdcdrv_mem_block_head *buf, dma_addr_t addr);
long hdcdrv_get_page_size(struct hdcdrv_cmd_get_page_size *cmd);

struct delayed_work *hdcdrv_get_recycle_mem(void);
struct device* hdcdrv_get_pdev_dev(int dev_id);
struct hdcdrv_dev_fmem *hdcdrv_get_dev_fmem_uni(void);

#endif /* _HDCDRV_MEM_COM_H_ */
